using System;
using System.Net.Mail;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.IO;

namespace Lds.Ward.PhotoDirectory.Logic
{
	public static class FlatFile
	{
		public static IEnumerable<Family> ToFamilies(string path)
		{
			using (StreamReader reader = new StreamReader(path))
				return ToFamilies(reader);
		}

		public static IEnumerable<Family> ToFamilies(TextReader reader)
		{
			reader.ReadLine(); // skip header row

			string famstr;
			while ((famstr = fetchNextFamily(reader))!=null)
			{
				Family family = new Family();
				
				family.Surname = GetFamilySurname(famstr);
				family.FamilyMembers.AddRange(GetFamilyMembers(famstr));
				family.MailingAddress = GetMailingAddress(famstr);
				IList<PhoneNumber> phones = GetPhoneNumbers(famstr);
				switch (phones.Count)
				{
					case 0:
						break;
					case 1:
						family.Telephone = phones[0];
						break;
					default:
						for (int i = 0; i < phones.Count && i < family.FamilyMembers.Count; i++)
							family.FamilyMembers[i].Telephone = phones[i];
						break;
				}
				IList<MailAddress> emails = GetEmailAddresses(famstr);
				switch (emails.Count)
				{
					case 0:
						break;
					case 1:
						family.Email = emails[0];
						break;
					default:
						for (int i = 0; i < emails.Count; i++)
							family.FamilyMembers[i].Email = emails[i];
						break;
				}

				yield return family;
			}
		}

		private const string prefix = @"      ";
		private const string addrRegex = @"(?<address>[^\t]+)";
		private const string emailRegex = @"(?<email>\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*)";

		private static PostalAddress GetMailingAddress(string famstr)
		{
			StringBuilder sb = new StringBuilder(80);
			Match m = Regex.Match(famstr, prefix + addrRegex);
			while (m.Success && !Regex.IsMatch(m.Groups["address"].Value, emailRegex) && !Regex.IsMatch(m.Groups["address"].Value, PhoneNumber.RegEx))
			{
				if (sb.Length > 0) sb.Append(Environment.NewLine);
				sb.Append(m.Groups["address"].Value);
				m = m.NextMatch();
			}
			return PostalAddress.Parse(sb.ToString());
		}

		private static IList<PhoneNumber> GetPhoneNumbers(string famstr)
		{
			List<PhoneNumber> phones = new List<PhoneNumber>();
			foreach (Match m in Regex.Matches(famstr, PhoneNumber.RegEx))
				phones.Add(new PhoneNumber(m.Groups["phone"].Value));
			return phones;
		}

		private static IList<MailAddress> GetEmailAddresses(string famstr)
		{
			List<MailAddress> emails = new List<MailAddress>();
			foreach (Match m in Regex.Matches(famstr, emailRegex))
				emails.Add(new MailAddress(m.Groups["email"].Value));
			return emails;
		}

		private static IEnumerable<FamilyMember> GetFamilyMembers(string famstr)
		{
			IList<FamilyMember> members = new List<FamilyMember>();

			MatchCollection matches = Regex.Matches(famstr, 
				@"^[^\t]+?\t(?<givennames>[^\t]+?)\t(?<gender>[MF])", RegexOptions.Multiline);
			foreach (Match m in matches)
			{
				FamilyMember member = new FamilyMember();
				member.GivenNames = m.Groups["givennames"].Value.Trim();
				member.Gender = (Gender)m.Groups["gender"].Value[0];
				member.Dependent = m.Groups["givennames"].Value.StartsWith(" ");
				members.Add(member);
			}

			return members;
		}

		private static string GetFamilySurname(string famstr)
		{
			Match m = Regex.Match(famstr, @"\A([^\t]+?)\t");
			if (!m.Success) throw new ApplicationException("Unrecognized data format.");
			return m.Groups[1].Value;
		}

		private static string fetchNextFamily(TextReader reader)
		{
			StringBuilder family = new StringBuilder(255);
			do
			{
				string line = reader.ReadLine() + Environment.NewLine;
				if (line != Environment.NewLine)
					family.Append(line);
			} while (family.Length > 0 && reader.Peek() == ' ');

			if (family.Length == 0) return null;
			return family.ToString();
		}
	}
}
